#pragma once

#include <iostream>
#include <vector>
#include <string>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <strings.h>

enum
{
    SOCKET_ERROR = 1,
    BIND_ERROR,
    ACCEPT_ERROR,
    SELECT_ERROR
};

class SelectServer
{
    static const int MAX_SZ = sizeof(fd_set) * 8;
public:
    SelectServer(uint16_t port)
        : _sockfds(MAX_SZ, -1)
    {
        _listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenfd < 0)
        {
            std::cerr << "socket error!" << std::endl;
            exit(SOCKET_ERROR);
        }

        struct sockaddr_in addr;
        bzero(&addr, sizeof(addr));
        addr.sin_family  = AF_INET;
        addr.sin_addr.s_addr = INADDR_ANY;
        addr.sin_port = htons(port);

        int n = bind(_listenfd, (struct sockaddr*)&addr, sizeof(addr));
        if (n < 0)
        {
            std::cerr << "bind error!" << std::endl;
            exit(BIND_ERROR);
        }

        _sockfds[0] = _listenfd;
        listen(_listenfd, 16);
    }

        void start()
    {
        while(true)
        {
            // 设置 set_fd
            fd_set fds; // 创建一个文件集
            FD_ZERO(&fds);
            int max_fd = _listenfd;

            // 把所有数组元素(套接字)加入到文件集
            for (int i = 0; i < MAX_SZ; i++)
            {
                if (_sockfds[i] == -1)
                    continue;

                FD_SET(_sockfds[i], &fds);
                max_fd = std::max(max_fd, _sockfds[i]);
            }

            // 设置 timeval
            struct timeval timeout = { 1, 0 };

            // select 监听
            int ret = select(max_fd + 1, &fds, nullptr, nullptr, &timeout);

            // 处理结果
            switch (ret)
            {
            case 0:
                std::clog << "timeout..." << std::endl;
                break;
            case -1:
                std::cerr << "select error..." << std::endl;
                exit(SELECT_ERROR);
                break;
            default:
                std::clog << "event happen..." << std::endl;
                handlerEvent(fds);
            }
        }
    }

private:
    void handlerEvent(fd_set& fds)
    {
        for (int i = 0; i < MAX_SZ; i++)
        {
            if (_sockfds[i] == -1 || !FD_ISSET(_sockfds[i], &fds))
                continue;

            if (_sockfds[i] == _listenfd)
                acceptClient();
            else
                serviceIO(i);
        }
    }

    void acceptClient()
    {
        // 接收连接
        struct sockaddr_in peer;
        bzero(&peer, sizeof(peer));
        socklen_t len;

        int clientfd = accept(_listenfd, (sockaddr*)&peer, &len);
        if (clientfd < 0)
        {
            std::cerr << "accept error!" << std::endl;
            exit(ACCEPT_ERROR);
        }

        // 把套接字插入 _sockfds 数组中
        int pos = 0;
        for (; pos < MAX_SZ; pos++)
        {
            if (_sockfds[pos] == -1)
                break;
        }

        if (pos == MAX_SZ)
        {
            std::clog << "select if full..." << std::endl;
            close(clientfd);
        }
        else
        {
            _sockfds[pos] = clientfd;
            std::clog << clientfd << " add to sockfds" << std::endl;
        }
    }

    void serviceIO(int pos)
    {
        // 接收数据
        char buffer[1024];
        int n = recv(_sockfds[pos], buffer, sizeof(buffer) - 1, 0);

        // 处理数据
        if (n > 0)
        {
            buffer[n] = '\0';
            std::cout << "message: " << buffer << std::endl;

            std::string ret = "echo: " + (std::string)buffer;
            send(_sockfds[pos], ret.c_str(), ret.size(), 0);
        }
        else if (n == 0)
        {
            std::clog << _sockfds[pos] << " exit..." << std::endl;
            close(_sockfds[pos]);
            _sockfds[pos] = -1;
        }
        else
        {
            std::cerr << _sockfds[pos] << " error..." << std::endl;
            close(_sockfds[pos]);
            _sockfds[pos] = -1;
        }
    }

private:
    int _listenfd;
    std::vector<int> _sockfds;
};
